﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using System.Runtime.InteropServices;

namespace Reflexive.IO.Transactions
{
    [Guid("3E8B8452-88E0-478A-876B-3AE834FFF88B")]
    public class ResourceManager<T>: IEnlistmentNotification,IObserver<T>
    {
        T _value;
        Action<T> _onNext;
        Action _onCompleted;
        Action<Exception> _onError;


        public ResourceManager(Action<T> onNext, Action onCompleted, Action<Exception> onError)
        {
            _onNext = onNext;
            _onError = onError;
            _onCompleted = onCompleted;
            
        }

        public virtual void Commit(Enlistment enlistment)
        {
            enlistment.Done();
        }

        public virtual void InDoubt(Enlistment enlistment)
        {
            enlistment.Done();
        }

        public virtual void Prepare(PreparingEnlistment preparingEnlistment)
        {
            try
            {
                if(_onNext != null)
                    _onNext(_value);
                preparingEnlistment.Prepared();
            }
            catch (Exception e)
            {
                preparingEnlistment.ForceRollback(e);
            }
        }

        public virtual void Rollback(Enlistment enlistment)
        {
            enlistment.Done();
        }

        public virtual void OnCompleted()
        {
            if(_onCompleted != null)
                _onCompleted();
        }

        public virtual void OnError(Exception error)
        {
            if(_onError != null)
                _onError(error);
        }

        public virtual void OnNext(T value)
        {
            _value = value;
            System.Transactions.Transaction.Current.EnlistDurable(Guid.Parse(((GuidAttribute)this.GetType().GetCustomAttributes(typeof(GuidAttribute), true).Single()).Value), this, EnlistmentOptions.None);
        }

        public static IObserver<T> Manage(IObserver<T> observer)
        {
            return new ResourceManager<T>(observer.OnNext, observer.OnCompleted, observer.OnError);
        }

        public static IObserver<T> Manage(Action<T> onNext)
        {
            return new ResourceManager<T>(onNext,null,null);
        }

        public static IObserver<T> Manage(Action<T> onNext, Action onCompleted)
        {
            return new ResourceManager<T>(onNext, onCompleted, null);
        }

        public static IObserver<T> Manage(Action<T> onNext, Action onCompleted, Action<Exception> onError)
        {
            return new ResourceManager<T>(onNext, onCompleted, onError);
        }

    }
}
